package mongoplu

import (
	"encoding/json"
	"fmt"
	"strings"

	"gitee.com/z1gotool/gorm2mongo/z1mongo"
	"gorm.io/gorm"
)

// https://www.cnblogs.com/xinliangcoder/p/14316509.html
// https://github.com/xinliangnote/go-gin-api/blob/master/internal/repository/mysql/plugin.go
// https://kgithub.com/xinliangnote/go-gin-api/blob/master/internal/repository/mysql/plugin.go

const (
	callBackBeforeName = "z1_to_mongo:before"
	callBackAfterName  = "z1_to_mongo:after"
)

// db.Use(&ToMongo{})
type ToMongo struct {
	EnableTables string // 【已经弃用，使用本插件后，默认全部表都走mongo】启用的表, 多个表通过英文逗号拆分, 并且字符串前后都有英文逗号, `,table1,table2,`
}

func (plugin *ToMongo) Name() string {
	return "toMongo"
}

func (plugin *ToMongo) Initialize(db *gorm.DB) (err error) {
	z1before := func(db *gorm.DB) {
		if goMongo(plugin, db) {
			// db.Statement.Table = fmt.Sprintf(`%v|OldDryRun=false`, db.Statement.Table)
			db.DryRun = true
		}
	}

	z1after := func(db *gorm.DB) {
		if goMongo(plugin, db) {
			{
				defer func() {
					db.DryRun = false // Must at last

					db.Statement.SQL.Reset()
					db.Statement.Vars = nil
				}()

				stmt := db.Statement
				sql := db.Dialector.Explain(stmt.SQL.String(), stmt.Vars...)
				isCount := false
				z1ret := stmt.Dest

				// log.Println(`------sql--1--`, sql)
				// sql = strings.ReplaceAll(sql, `|OldDryRun=false`, ``)
				// log.Println(`------sql--2--`, sql)

				if strings.HasPrefix(sql, `SELECT `) {
					if strings.Contains(sql, `count(`) {
						isCount = true
					}

					{
						b, err := json.Marshal(stmt.Dest)
						if err != nil {
							db.Error = err
							return
						}
						destStr := string(b)
						// log.Println(`------------destStr----------`, destStr)
						if !strings.HasPrefix(destStr, `[`) {
							sql = sql + ` LIMIT 1`
						}
					}
				}

				// log.Println(`------sql--2--`, sql)

				// db = gorm2mongo.UpdateDBContext(`z1mongoShowQuery`, true, db, false)
				showQuery := false
				tmp := db.Statement.Context.Value(`z1mongoShowQuery`)
				if tmp != nil {
					if b, ok := tmp.(bool); ok {
						showQuery = b
					}
				}

				// log.Println(`-------showQuery-----`, showQuery)

				_, total, _, err := z1mongo.Sql2Mongo(db, z1mongo.DB, sql, isCount, showQuery, z1ret)

				if err != nil {
					db.Error = err
					return
				}

				db.RowsAffected = total

				if isCount {
					stmt.Dest = &total
					return
				}
			}
		}
	}

	// before
	// _ = db.Callback().Create().Before("gorm:before_create").Register(callBackBeforeName, before)
	// _ = db.Callback().Query().Before("gorm:query").Register(callBackBeforeName, before)
	// _ = db.Callback().Delete().Before("gorm:before_delete").Register(callBackBeforeName, before)
	// _ = db.Callback().Update().Before("gorm:setup_reflect_value").Register(callBackBeforeName, before)
	// _ = db.Callback().Row().Before("gorm:row").Register(callBackBeforeName, before)
	// _ = db.Callback().Raw().Before("gorm:raw").Register(callBackBeforeName, before)
	// _ = db.Callback().Create().Before("gorm:before_create").Register(callBackBeforeName, before)
	db.Callback().Create().Before("gorm:before_create").Register(callBackBeforeName, z1before)
	db.Callback().Query().Before("gorm:query").Register(callBackBeforeName, z1before)
	db.Callback().Delete().Before("gorm:before_delete").Register(callBackBeforeName, z1before)
	db.Callback().Update().Before("gorm:setup_reflect_value").Register(callBackBeforeName, z1before)

	// after
	// _ = db.Callback().Create().After("gorm:after_create").Register(callBackAfterName, after)
	// _ = db.Callback().Query().After("gorm:after_query").Register(callBackAfterName, after)
	// _ = db.Callback().Delete().After("gorm:after_delete").Register(callBackAfterName, after)
	// _ = db.Callback().Update().After("gorm:after_update").Register(callBackAfterName, after)
	// _ = db.Callback().Row().After("gorm:row").Register(callBackAfterName, after)
	// _ = db.Callback().Raw().After("gorm:raw").Register(callBackAfterName, after)
	db.Callback().Create().After("gorm:after_create").Register(callBackAfterName, z1after)
	db.Callback().Query().After("gorm:after_query").Register(callBackAfterName, z1after)
	db.Callback().Delete().After("gorm:after_delete").Register(callBackAfterName, z1after)
	db.Callback().Update().After("gorm:after_update").Register(callBackAfterName, z1after)

	return
}

func goMongo(plugin *ToMongo, db *gorm.DB) (yes bool) {
	return true

	// db = gorm2mongo.UpdateDBContext(`z1mongoAllToMongo`, true, db, false)
	tmp := db.Statement.Context.Value(`z1mongoAllToMongo`)
	// log.Println(`-----z1mongoAllToMongo---------`, tmp)

	if tmp != nil {
		if b, ok := tmp.(bool); ok {
			if b {
				return true
			}
		}
	}

	currentTable := fmt.Sprintf(`,%v,`, db.Statement.Table)
	// currentTable = strings.ReplaceAll(currentTable, `|OldDryRun=false`, ``)
	if strings.Contains(plugin.EnableTables, currentTable) {
		yes = true
	}

	return
}
